package com.virecog.recog;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream.GetField;
import java.sql.ResultSet;

import org.encog.Encog;
import org.encog.engine.network.activation.ActivationSigmoid;
import org.encog.ml.data.MLData;
import org.encog.ml.data.MLDataPair;
import org.encog.ml.data.MLDataSet;
import org.encog.ml.data.basic.BasicMLDataSet;
import org.encog.neural.networks.BasicNetwork;
import org.encog.neural.networks.layers.BasicLayer;
import org.encog.neural.networks.training.propagation.back.Backpropagation;
import org.encog.persist.EncogDirectoryPersistence;

import com.virecog.symbol.SymbolDetection;
import com.virecog.utils.Language;
import com.virecog.utils.Utils;

public class Recognition {

	String imagepath;
	String language;

	public Recognition(String imagepath, String language) {
		// TODO Auto-generated constructor stub
		this.imagepath = imagepath;
		this.language = language;
	}

	/**
	 * load encog neural network form file
	 * 
	 * @param filename
	 * @return
	 */
	public BasicNetwork loadNeuralNetwork(String filename) {

		return (BasicNetwork) EncogDirectoryPersistence.loadObject(new File(
				filename));
		
	}

	/**
	 * 
	 * @return
	 */
	public String doEncogRecognition() {

		System.out.println("Recognition .....");

		String neuralfile = Language.getNeuralTrainedFile(language);
		if (neuralfile == null) {
			System.out.println("Not support" + language);
			return null;
		} else {

			BasicNetwork network = loadNeuralNetwork(neuralfile);

			if (network == null) {
				System.out.println("load neural network error");
				return null;
			} else {
				StringBuffer resultstr = new StringBuffer();

				SymbolDetection detect = new SymbolDetection(imagepath);
				double[][] input = detect.getInputForEncogNetwork();

				MLDataSet data = new BasicMLDataSet(input, null);
				
				for (MLDataPair pair : data) {
					final MLData result = network.compute(pair.getInput());
					char ch = Utils.bitArrayToChar(result.getData());
					resultstr.append(ch);
				}
				// return recognized characters
				return resultstr.toString();
			}
		}
	}

	/************************** training *************************************/
	/**
	 * read desire text
	 * 
	 * @param filepath
	 * @return
	 */
	public static String readDesireResult(String filepath) {
		File file = new File(filepath);
		{
			// FileInputStream inf = new FileInputStream(file);
			StringBuffer buffer = new StringBuffer();
			try {
				BufferedReader reader = new BufferedReader(new FileReader(file));
				String line;
				while ((line = reader.readLine()) != null) {
					if (line != null)
						buffer.append(line);
				}
				return buffer.toString();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			}
		}
	}

	/**
	 * desired output for encog neural network
	 * 
	 * @param desireTextPath
	 * @return
	 */
	public static double[][] getOutputForEncogNetwork(String desireTextPath) {
		String desireText = readDesireResult(desireTextPath);
		double[][] outputs = new double[desireText.length()][16];
		System.out.println("Desire size:" + desireText.length());
		for (int i = 0; i < desireText.length(); i++) {
			double[] output = Utils.charToBitarray(desireText.charAt(i));
			outputs[i] = output;
		}

		return outputs;
	}

	/**
	 * 
	 * @param inputImagePath
	 * @param outputImagePath
	 * @param language
	 */
	public static void doEncogTraining(String inputImagePath,
			String desiredOutputPath, String language) {

		double[][] input = new SymbolDetection(inputImagePath)
				.getInputForEncogNetwork();
		double[][] output = getOutputForEncogNetwork(desiredOutputPath);

		BasicNetwork network = new BasicNetwork();

		network.addLayer(new BasicLayer(new ActivationSigmoid(), true, 150));
		network.addLayer(new BasicLayer(new ActivationSigmoid(), true, 250));
		network.addLayer(new BasicLayer(new ActivationSigmoid(), true, 16));
		network.getStructure().finalizeStructure();
		network.reset();

		MLDataSet trainingSet = new BasicMLDataSet(input, output);

		Backpropagation train = new Backpropagation(network, trainingSet, 0.5,
				0.3);

		int epoch = 1;

		do {
			train.iteration();
			System.out
					.println("Epoch #" + epoch + " Error:" + train.getError());
			epoch++;
		} while (train.getError() > 0.001);

		System.out.println("network error: "+network.calculateError(trainingSet));
		
		// save neural network to eg file
		String langdetail = Language.getLanguageDetail(language);
		File file = new File("traindata/" + langdetail + ".eg");

		EncogDirectoryPersistence.saveObject(file, network);
		Encog.getInstance().shutdown();

	}

	public static void main(String[] args) {
		Recognition.doEncogTraining("images/english.png", "images/english.txt",
				"eng");

		Recognition recog = new Recognition("images/english.png", "eng");
		String result = recog.doEncogRecognition();
		System.out.println(result);
		String desire = Recognition.readDesireResult("images/english.txt");
		
		int count = 0;
		for(int i = 0; i< result.length(); i++){
			if(result.charAt(i) != desire.charAt(i)){
				System.out.println(desire.charAt(i) + " -> "+result.charAt(i));
				count++;
			}
		}
		System.out.println("Accuracy: "+(float)count/result.length());
	}
}
